package com.xiaojf.xlog.aspect;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.xiaojf.xlog.annotation.AccessLogAnnotation;
import com.xiaojf.xlog.appender.AccessLogAppender;
import com.xiaojf.xlog.model.XlogAccessLog;
import com.xiaojf.xlog.util.AopAnnotationUtil;
import com.xiaojf.xlog.util.HttpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 访问日志切面
 *
 * @author Ade.Xiao 2021/1/12 17:33
 */
@Aspect
public class AccessLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(AccessLogAspect.class);
    @Autowired
    private AccessLogAppender accessLogAppender;

    /**
     * 构造函数
     * 切面规则
     *
     * @author Ade.Xiao 2021/1/12 17:51
     */
    @Pointcut("@annotation(com.xiaojf.xlog.annotation.AccessLogAnnotation)")
    public void accessLogAspect() {
    }

    /**
     * 环绕通知
     *
     * @author Ade.Xiao 2021/1/12 17:51
     */
    @Around("accessLogAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        AccessLogAnnotation accessLogAnnotation = AopAnnotationUtil.getAnnotation(joinPoint, AccessLogAnnotation.class);

        long start = System.currentTimeMillis();
        // 执行方法
        Object obj = joinPoint.proceed();
        long end = System.currentTimeMillis();

        XlogAccessLog log = new XlogAccessLog();
        log.setAccessTime(System.currentTimeMillis());
        log.setAppendTime(end - start);
        log.setSuccessful(true);
        log.setIp(HttpUtil.getIPAddress(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()));

        if (StrUtil.isNotBlank(accessLogAnnotation.success())) {
            // 获取注解上下文
            EvaluationContext evaluationContext = AopAnnotationUtil.getEvaluationContext(joinPoint);
            // 解析spring el 表达式
            String content = AopAnnotationUtil.getValue(evaluationContext, accessLogAnnotation.success(), String.class);
            log.setContent(ObjectUtil.defaultIfNull(content, ""));
        }
        log.setOperateType(accessLogAnnotation.operateType());

        // 调用日志服务，保存日志信息
        accessLogAppender.record(log);
        return obj;
    }

    /**
     * 异常通知 用于拦截记录异常日志
     *
     * @param joinPoint
     * @param e
     * @author Ade.Xiao 2021/1/12 17:51
     */
    @AfterThrowing(pointcut = "accessLogAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) throws ClassNotFoundException {
        AccessLogAnnotation accessLogAnnotation = AopAnnotationUtil.getAnnotation(joinPoint, AccessLogAnnotation.class);

        XlogAccessLog log = new XlogAccessLog();
        log.setAccessTime(System.currentTimeMillis());
        log.setIp(HttpUtil.getIPAddress(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()));
        log.setSuccessful(false);

        if (StrUtil.isNotBlank(accessLogAnnotation.fail())) {
            // 获取注解上下文
            EvaluationContext evaluationContext = AopAnnotationUtil.getEvaluationContext(joinPoint);
            // 解析spring el 表达式
            String content = AopAnnotationUtil.getValue(evaluationContext, accessLogAnnotation.fail(), String.class);
            log.setContent(ObjectUtil.defaultIfNull(content, ""));
        }

        log.setOperateType(accessLogAnnotation.operateType());
        log.setError(e.getMessage());
        // 调用日志服务，保存日志信息
        accessLogAppender.record(log);
    }
}